package com.matrix.framework.sysconfig.iplist.service.impl;

import com.matrix.framework.sysconfig.iplist.data.IpAccessRecordPo;
import com.matrix.framework.sysconfig.iplist.data.IpSecurityRuleDto;
import com.matrix.framework.sysconfig.iplist.data.IpSecurityRulePo;
import com.matrix.framework.sysconfig.iplist.repository.IpAccessRecordRepository;
import com.matrix.framework.sysconfig.iplist.repository.IpSecurityRuleRepository;
import com.matrix.framework.sysconfig.iplist.service.IpSecurityService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * IP安全服务实现类
 *
 * Copyright © 海平面工作室 版权所有
 *
 * @Author: Leo
 * @Create: 2024/12/1 15:30
 * @Since 1.2
 */
@Service
public class IpSecurityServiceImpl implements IpSecurityService {

    private final IpSecurityRuleRepository ruleRepository;
    private final IpAccessRecordRepository accessRecordRepository;

    // 内存缓存，用于频率控制
    private final ConcurrentHashMap<String, AccessCounter> accessCounters = new ConcurrentHashMap<>();

    // IP地址验证正则表达式
    //private static final Pattern IP_PATTERN = Pattern.compile("^([0-9]{1,3}\\.){3}[0-9]{1,3}$");
    private static final Pattern IP_PATTERN = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|(?:[0-9a-fA-F]{1,4}:){7}(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$");
    private static final Pattern CIDR_PATTERN = Pattern.compile("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$");

    public IpSecurityServiceImpl(IpSecurityRuleRepository ruleRepository, IpAccessRecordRepository accessRecordRepository) {
        this.ruleRepository = ruleRepository;
        this.accessRecordRepository = accessRecordRepository;
    }

    @Override
    public Mono<IpValidationResult> validateIpAccess(String ipAddress, String username, String apiPath) {
        return ruleRepository.findEnabledRules()
                .collectList()
                .flatMap(rules -> {
                    // 检查IP格式
                    if (!isValidIpAddress(ipAddress)) {
                        return Mono.just(new IpValidationResult(false, "IP地址格式无效", "DENY", null));
                    }

                    // 检查黑名单
                    for (IpSecurityRulePo rule : rules) {
                        if ("BLACKLIST".equals(rule.getRuleType()) && isIpMatch(ipAddress, rule.getIpPattern())) {
                            return Mono.just(new IpValidationResult(false, "IP地址在黑名单中", "DENY", rule));
                        }
                    }

                    // 检查白名单
                    boolean hasWhitelistRule = false;
                    boolean whitelistMatch = false;
                    for (IpSecurityRulePo rule : rules) {
                        if ("WHITELIST".equals(rule.getRuleType())) {
                            hasWhitelistRule = true;
                            if (isIpMatch(ipAddress, rule.getIpPattern())) {
                                whitelistMatch = true;
                                break;
                            }
                        }
                    }

                    // 如果有白名单规则但IP不在白名单中，则拒绝访问
                    if (hasWhitelistRule && !whitelistMatch) {
                        return Mono.just(new IpValidationResult(false, "IP地址不在白名单中", "DENY", null));
                    }

                    // 检查频率限制
                    return checkRateLimit(ipAddress, username, apiPath)
                            .flatMap(rateLimitResult -> {
                                if (!rateLimitResult.isAllowed()) {
                                    return Mono.just(new IpValidationResult(false, rateLimitResult.getReason(), "RATE_LIMIT", null));
                                }
                                return Mono.just(new IpValidationResult(true, "IP访问验证通过", "ALLOW", null));
                            });
                });
    }

    @Override
    public Mono<Void> recordIpAccess(IpAccessRecordPo record) {
        return accessRecordRepository.save(record)
                .then();
    }

    @Override
    public Mono<RateLimitResult> checkRateLimit(String ipAddress, String username, String apiPath) {
        String key = ipAddress + ":" + username + ":" + apiPath;
        AccessCounter counter = accessCounters.computeIfAbsent(key, k -> new AccessCounter());

        long currentTime = System.currentTimeMillis();
        counter.updateCount(currentTime);

        // 获取该IP的规则
        return ruleRepository.findByIpPatternAndType(ipAddress, "RATE_LIMIT")
                .collectList()
                .flatMap(rules -> {
                    for (IpSecurityRulePo rule : rules) {
                        if (rule.getMaxRequestsPerMinute() != null && counter.getMinuteCount() > rule.getMaxRequestsPerMinute()) {
                            return Mono.just(new RateLimitResult(false, "每分钟请求次数超限", 0, counter.getMinuteResetTime()));
                        }
                        if (rule.getMaxRequestsPerHour() != null && counter.getHourCount() > rule.getMaxRequestsPerHour()) {
                            return Mono.just(new RateLimitResult(false, "每小时请求次数超限", 0, counter.getHourResetTime()));
                        }
                        if (rule.getMaxRequestsPerDay() != null && counter.getDayCount() > rule.getMaxRequestsPerDay()) {
                            return Mono.just(new RateLimitResult(false, "每天请求次数超限", 0, counter.getDayResetTime()));
                        }
                    }

                    // 默认限制：每分钟100次，每小时1000次，每天10000次
                    if (counter.getMinuteCount() > 100) {
                        return Mono.just(new RateLimitResult(false, "每分钟请求次数超限", 0, counter.getMinuteResetTime()));
                    }
                    if (counter.getHourCount() > 1000) {
                        return Mono.just(new RateLimitResult(false, "每小时请求次数超限", 0, counter.getHourResetTime()));
                    }
                    if (counter.getDayCount() > 10000) {
                        return Mono.just(new RateLimitResult(false, "每天请求次数超限", 0, counter.getDayResetTime()));
                    }

                    return Mono.just(new RateLimitResult(true, "频率检查通过", 
                            Math.max(0, 100 - counter.getMinuteCount()), counter.getMinuteResetTime()));
                });
    }

    @Override
    public Mono<IpSecurityRulePo> saveRule(IpSecurityRulePo rule) {
        if (rule.getId() == null) {
            rule.setCreateTime(System.currentTimeMillis());
        }
        rule.setUpdateTime(System.currentTimeMillis());
        return ruleRepository.save(rule);
    }

    @Override
    public Mono<Void> deleteRule(Long id) {
        return ruleRepository.deleteById(id);
    }

    @Override
    public Flux<IpSecurityRulePo> getAllRules() {
        return ruleRepository.findAll();
    }

    @Override
    public Mono<IpSecurityRuleDto> findRules(IpSecurityRuleDto dto) {
        // 如果dto为null，创建默认dto
        if (dto == null) {
            dto = new IpSecurityRuleDto();
        }
        
        final IpSecurityRuleDto finalDto = dto; // 为了在lambda中使用
        
        return ruleRepository.findAll()
                .filter(rule -> {
                    // 按规则类型过滤
                    if (finalDto.getRuleType() != null && !finalDto.getRuleType().trim().isEmpty()) {
                        if (!finalDto.getRuleType().equalsIgnoreCase(rule.getRuleType())) {
                            return false;
                        }
                    }
                    
                    // 按目标类型过滤
                    if (finalDto.getTargetType() != null && !finalDto.getTargetType().trim().isEmpty()) {
                        if (!finalDto.getTargetType().equalsIgnoreCase(rule.getTargetType())) {
                            return false;
                        }
                    }
                    
                    // 按启用状态过滤
                    if (finalDto.getEnabled() != null) {
                        if (!finalDto.getEnabled().equals(rule.getEnabled())) {
                            return false;
                        }
                    }
                    
                    // 按IP模式过滤（模糊匹配）
                    if (finalDto.getIpPattern() != null && !finalDto.getIpPattern().trim().isEmpty()) {
                        if (rule.getIpPattern() == null || 
                            !rule.getIpPattern().toLowerCase().contains(finalDto.getIpPattern().toLowerCase())) {
                            return false;
                        }
                    }
                    
                    return true;
                })
                .collectList()
                .map(filteredRules -> {
                    // 获取过滤后的数据总数
                    long filteredTotal = filteredRules.size();
                    
                    // 分页参数处理
                    int page = finalDto.getPage() > 0 ? finalDto.getPage() : 1;
                    int pageSize = finalDto.getPageSize() > 0 ? finalDto.getPageSize() : 10;
                    if (pageSize > 100) pageSize = 100;
                    
                    // 计算分页
                    int startIndex = (page - 1) * pageSize;
                    int endIndex = Math.min(startIndex + pageSize, filteredRules.size());
                    
                    // 确保索引不越界
                    if (startIndex >= filteredRules.size()) {
                        startIndex = 0;
                        endIndex = 0;
                    }
                    
                    List<IpSecurityRulePo> pageRecords = filteredRules.subList(startIndex, endIndex);
                    
                    // 创建新的DTO对象返回，避免修改输入参数
                    IpSecurityRuleDto result = new IpSecurityRuleDto();
                    result.setRuleType(finalDto.getRuleType());
                    result.setTargetType(finalDto.getTargetType());
                    result.setEnabled(finalDto.getEnabled());
                    result.setIpPattern(finalDto.getIpPattern());
                    result.setItems(pageRecords);
                    result.setTotal(filteredTotal);
                    result.setPage(page);
                    result.setPageSize(pageSize);
                    
                    return result;
                });
    }

    @Override
    public Mono<Map<String, Object>> getIpStatistics(String ipAddress, int days) {
        long startTime = System.currentTimeMillis() - (days * 24 * 60 * 60 * 1000L);
        
        return accessRecordRepository.findByIpAddressAndTimeRange(ipAddress, startTime)
                .collectList()
                .map(records -> {
                    Map<String, Object> stats = new HashMap<>();
                    stats.put("totalRequests", records.size());
                    stats.put("successRequests", records.stream().filter(r -> "SUCCESS".equals(r.getResult())).count());
                    stats.put("failedRequests", records.stream().filter(r -> "FAILED".equals(r.getResult())).count());
                    stats.put("blockedRequests", records.stream().filter(r -> "BLOCKED".equals(r.getResult())).count());
                    
                    // 计算平均响应时间
                    double avgResponseTime = records.stream()
                            .filter(r -> r.getResponseTime() != null)
                            .mapToLong(IpAccessRecordPo::getResponseTime)
                            .average()
                            .orElse(0.0);
                    stats.put("avgResponseTime", avgResponseTime);
                    
                    return stats;
                });
    }

    @Override
    public Mono<Long> cleanExpiredRecords(int days) {
        long cutoffTime = System.currentTimeMillis() - (days * 24 * 60 * 60 * 1000L);
        return accessRecordRepository.deleteByCreateTimeBefore(cutoffTime);
    }

    /**
     * 验证IP地址格式
     */
    private boolean isValidIpAddress(String ipAddress) {
        if (ipAddress == null || ipAddress.trim().isEmpty()) {
            return false;
        }
        return IP_PATTERN.matcher(ipAddress.trim()).matches();
    }

    /**
     * 检查IP是否匹配模式
     */
    private boolean isIpMatch(String ipAddress, String pattern) {
        if (pattern == null || pattern.trim().isEmpty()) {
            return false;
        }

        // 检查是否为CIDR格式
        if (CIDR_PATTERN.matcher(pattern).matches()) {
            return isIpInCidrRange(ipAddress, pattern);
        }

        // 检查是否为IP段格式（如：192.168.1.*）
        if (pattern.contains("*")) {
            return isIpInWildcardRange(ipAddress, pattern);
        }

        // 精确匹配
        return ipAddress.equals(pattern);
    }

    /**
     * 检查IP是否在CIDR范围内
     */
    private boolean isIpInCidrRange(String ipAddress, String cidr) {
        try {
            String[] parts = cidr.split("/");
            String networkAddress = parts[0];
            int prefixLength = Integer.parseInt(parts[1]);

            InetAddress network = InetAddress.getByName(networkAddress);
            InetAddress ip = InetAddress.getByName(ipAddress);

            byte[] networkBytes = network.getAddress();
            byte[] ipBytes = ip.getAddress();

            if (networkBytes.length != ipBytes.length) {
                return false;
            }

            int numBytes = prefixLength / 8;
            int numBits = prefixLength % 8;

            // 检查完整字节
            for (int i = 0; i < numBytes; i++) {
                if (networkBytes[i] != ipBytes[i]) {
                    return false;
                }
            }

            // 检查部分字节
            if (numBits > 0) {
                int mask = (0xFF << (8 - numBits)) & 0xFF;
                if ((networkBytes[numBytes] & mask) != (ipBytes[numBytes] & mask)) {
                    return false;
                }
            }

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 检查IP是否在通配符范围内
     */
    private boolean isIpInWildcardRange(String ipAddress, String pattern) {
        String regex = pattern.replace(".", "\\.").replace("*", "[0-9]{1,3}");
        return Pattern.matches(regex, ipAddress);
    }

    /**
     * 访问计数器内部类
     */
    private static class AccessCounter {
        private int minuteCount = 0;
        private int hourCount = 0;
        private int dayCount = 0;
        private long minuteResetTime;
        private long hourResetTime;
        private long dayResetTime;

        public void updateCount(long currentTime) {
            // 检查是否需要重置分钟计数
            if (currentTime >= minuteResetTime) {
                minuteCount = 1;
                minuteResetTime = currentTime + 60000; // 1分钟后重置
            } else {
                minuteCount++;
            }

            // 检查是否需要重置小时计数
            if (currentTime >= hourResetTime) {
                hourCount = 1;
                hourResetTime = currentTime + 3600000; // 1小时后重置
            } else {
                hourCount++;
            }

            // 检查是否需要重置天计数
            if (currentTime >= dayResetTime) {
                dayCount = 1;
                dayResetTime = currentTime + 86400000; // 1天后重置
            } else {
                dayCount++;
            }
        }

        public int getMinuteCount() {
            return minuteCount;
        }

        public int getHourCount() {
            return hourCount;
        }

        public int getDayCount() {
            return dayCount;
        }

        public long getMinuteResetTime() {
            return minuteResetTime;
        }

        public long getHourResetTime() {
            return hourResetTime;
        }

        public long getDayResetTime() {
            return dayResetTime;
        }
    }
} 